package models

import (
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"gitchina/kly_service/data"
	"gitchina/kly_service/errors"
	"gitchina/kly_service/util"
	"regexp"
	"strings"
	"time"

	_ "github.com/astaxie/beego/orm"
)

type Ad struct {
	PicUrl  string `json:"pic_url"`
	HtmlUr  string `json:"html_url"`
	SortNum int    `json:"sort_num"`
}

type DeviceInfo struct {
	Id    string `json:"id"`
	OS    string `json:"os"`
	Model string `json:"model"`
}

type ClientErrorLog struct {
	OS   string `json:"os"`
	UA   string `json:"ua"`
	Type string `json:"type"`
	Code string `json:"code"`
	Url  string `json:"url"`
	Info string `json:"info"`
	Time string `json:"time"`
}

type Bank struct {
	Id   string "id"
	Name string `name`
}

type ClientVersion struct {
	MinVersion     string `json:"min_ver"`
	CurrentVersion string `json:"curr_ver"`
}

type PagedRQ struct {
	PageNo   int `json:"page_no"`
	PageSize int `json:"page_size"`
}

type Dict struct {
	Code      string `json:"code"`
	Name      string `json:"name"`
	SortOrder string `json:"sort_order"`
}

type General struct{}

var device_salt = `*(#_@2MFI`
var clientVer ClientVersion
var generalCache = util.NewCacheL(util.General_Cache, util.CacheType(), util.ValueReloader{
	Fn:       func() interface{} { return getAdList() },
	Interval: 5 * time.Minute,
	Key:      util.Ad_Cache_Key,
}, util.ValueReloader{
	Fn:       func() interface{} { return getParams() },
	Interval: 10 * time.Minute,
	Key:      util.Param_Cache_Key,
}, util.ValueReloader{
	Fn:       func() interface{} { return getDicts() },
	Interval: 10 * time.Minute,
	Key:      util.Dict_Cache_Key,
}, util.ValueReloader{
	Fn:       func() interface{} { return getBlackIpList() },
	Interval: 1 * time.Minute,
	Key:      util.IP_Block_List_Cache_Key,
}, util.ValueReloader{
	Fn:       func() interface{} { return getBankList() },
	Interval: 30 * time.Minute,
	Key:      util.Bank_List_Cache_Key,
}, util.ValueReloader{
	Fn:       func() interface{} { return getDicts2() },
	Interval: 10 * time.Minute,
	Key:      util.Dict_Cache_Key2,
})

func GeneralInit() {
	var g General
	g.GetAdList()
	g.GetParams("")
	g.GetDict("")
	g.IsIpBlocked("")
	g.GetBankList()
}

func (g General) GetDict(parentId string) map[string]string {
	val := getCachedVal(util.Dict_Cache_Key, func() interface{} { return getDicts() })
	dicts := val.(map[string]map[string]string)
	c := dicts[parentId]
	return c
}

func (g General) GetDict2(parentId string) []Dict {
	val := getCachedVal(util.Dict_Cache_Key2, func() interface{} { return getDicts2() })
	dicts := val.(map[string][]Dict)
	c := dicts[parentId]
	return c
}

func (g General) GetParams(key string) string {
	val := getCachedVal(util.Param_Cache_Key, func() interface{} { return getParams() })
	params := val.(map[string]string)
	c := params[key]
	return c

}

func (g General) GetAdList() []Ad {
	val := getCachedVal(util.Ad_Cache_Key, func() interface{} { return getAdList() })
	return val.([]Ad)
}

func (g General) IsIpBlocked(ip string) bool {
	val := getCachedVal(util.IP_Block_List_Cache_Key, func() interface{} { return getBlackIpList() })
	ipBlackList := val.(map[string]bool)
	val, ok := ipBlackList[ip]
	return ok
}

func (g General) GetBankList() []Bank {
	val := getCachedVal(util.Bank_List_Cache_Key, func() interface{} { return getBankList() })
	return val.([]Bank)
}

func (g General) GetClientId(device DeviceInfo, profile string) (clientId string, err error) {
	var repo data.GeneralRepo
	if len(device.Id) != 0 {
		clientId = fmt.Sprintf("1%x", sha1.Sum([]byte(fmt.Sprintf("%s%s", device.Id, device_salt))))
		if err = repo.AddClientId(clientId, profile); err != nil {
			util.GetLogger().Error("[model-GetClientId] - error:%s", err.Error())
		}
	}
	return
}

func (g General) AddClientErrorLog(errorLogs string) {
	//format : os|ua|type|code|url|info|time\n
	var repo data.GeneralRepo
	split := strings.Split(errorLogs, "\n")
	logList := make([]data.ClientLogDto, 0)
	for _, log := range split {
		if len(log) > 0 {
			dto := data.ClientLogDto{
				Name:     "Client",
				Message:  log,
				ServerIp: "::1",
			}
			sepIdx := strings.LastIndex(log, "|")
			if sepIdx != -1 && sepIdx+1 < len(log) {
				dto.Time = log[sepIdx+1 : len(log)]
			}
			logList = append(logList, dto)
		}
	}
	if len(logList) > 0 {
		if err := repo.AddClientErrorLogs(logList); err != nil {
			util.GetLogger().Error("[model-AddClientErrorLog] - error:%s", err.Error())
		}
	}
}

func (g General) CheckVersion(ver string) (err error) {
	var ok bool
	if ok, _ = regexp.Match(`^\d+\.\d+\.\d+$`, []byte(ver)); ok {
		if ok = strings.Compare(ver, clientVer.CurrentVersion) > 0; ok {
			err = errors.New(errors.MSG_General_Invalid_Ver)
		} else {
			if ok = strings.Compare(ver, clientVer.MinVersion) >= 0; !ok {
				err = errors.New(errors.MSG_General_Too_Low_Ver)
			}
		}
	} else {
		err = errors.New(errors.MSG_General_Invalid_Ver)
	}
	return
}

func getCachedVal(key string, loadFn func() interface{}) (val interface{}) {
	var ok bool
	if val, ok = generalCache.Get(key); !ok {
		val = loadFn()
		generalCache.Set(key, val)
	}
	return val
}

func getAdList() interface{} {
	var repo data.GeneralRepo
	list := repo.GetAds(6)
	ad_list := make([]Ad, len(list))
	for idx, item := range list {
		ad_list[idx] = Ad{PicUrl: item.PicUrl, HtmlUr: item.HtmlUrl, SortNum: item.SortNum}
	}
	return ad_list
}

func getParams() interface{} {
	var repo data.GeneralRepo
	para_map := make(map[string]string)
	for _, list := range repo.GetParas() {
		para_map[list[0].(string)] = list[1].(string)
	}
	if ver, ok := para_map["version"]; ok {
		parseClientVersion(ver)
	}

	return para_map
}

func getDicts() interface{} {
	var dict_map = make(map[string]map[string]string)
	var code_name_map map[string]string
	var repo data.GeneralRepo
	for _, list := range repo.GetDicts() {
		parentId := list[2].(string)
		if parentId != "" {
			if _, ok := dict_map[parentId]; !ok {
				code_name_map = make(map[string]string)
				dict_map[parentId] = code_name_map
			}
			code_name_map[list[1].(string)] = list[0].(string)
		}
	}
	return dict_map
}

func getDicts2() interface{} {
	var dict_map = make(map[string][]Dict)
	var code_name_list []Dict
	var repo data.GeneralRepo
	for _, list := range repo.GetDicts() {
		parentId := list[2].(string)
		if parentId != "" {
			if _, ok := dict_map[parentId]; !ok {
				code_name_list = make([]Dict, 0)
			}
			code_name_list = append(code_name_list, Dict{
				Code:      list[1].(string),
				Name:      list[0].(string),
				SortOrder: list[3].(string),
			})
			dict_map[parentId] = code_name_list
		}
	}
	return dict_map
}

func getBlackIpList() interface{} {
	var repo data.GeneralRepo
	blackList := make(map[string]bool)
	for _, v := range repo.GetBlockIps() {
		blackList[v.(string)] = true
	}
	return blackList
}

func getBankList() interface{} {
	var repo data.GeneralRepo
	lists := repo.GetBankList()
	bankList := make([]Bank, len(lists))
	for i, v := range lists {
		bankList[i] = Bank{Id: v[0].(string), Name: v[1].(string)}
	}
	return bankList
}

func parseClientVersion(ver string) {
	json.Unmarshal([]byte(ver), &clientVer)
}
